<?php

class Placement extends DataObject implements i18nEntityProvider {
    private static $singular_name = "Placement";
    private static $plural_name = "Placements";
    
    private static $db = array(
        'SetOption' => "Enum(array('Auto Weak Leg', 'Auto Left', 'Auto Right'), 'Auto Weak Leg')"
    );

	private static $has_one = array(
		'Place' => 'Member'
	);
    
    private static $root_username = 'root';
    
    private static $direct_child_limit = 2;
    
    private static $show_level = 4;
    
    private static $extensions = array(
        "HierarchyTree"
    );
	
	 private static $casting = array(
        "TotalLeftGroupDownline" => "Int",
        "TotalRightGroupDownline" => "Int"
    );
    
    static function set_root_username($username){
    	Config::inst()->update('Placement', 'root_username', $username);
    }
    
    static function get_root_username(){
        return Config::inst()->get('Placement', 'root_username');
    }
    
    static function set_direct_child_limit($limit){
    	Config::inst()->update('Placement', 'direct_child_limit', $limit);
    }
    
    static function get_direct_child_limit(){
        return Config::inst()->get('Placement', 'direct_child_limit');
    }
    
    static function set_show_level($level){
    	Config::inst()->update('Placement', 'show_level', $level);
    }
    
    static function get_show_level(){
        return Config::inst()->get('Placement', 'show_level');
    }
    
    static function get_placement_option_by_id($id){
        return DB::query("select SetOption from Placement where MemberID = '".$id."'")->value();
    }
    
    static function get_placement_option_by_username($username){
        return DB::query("select Placement.SetOption from Placement inner join Member on Placement.MemberID = Member.ID where Member.Username = '".$username."'")->value();
    }
    
    static function get_placement_option_by_email($email){
        return DB::query("select Placement.SetOption from Placement inner join Member on Placement.MemberID = Member.ID where Member.Email = '".$email."'")->value();
    }
    
    static function tree_level_node($id){
        $placement = DataObject::get_by_id('Placement', (int)$id);
        if(!$placement) return array();
        
        $result = array();
        $metadata = array();
        $title = '';
        
        $query = new SQLQuery();
        $query->select = array(
            'NLevel',
            'count(ID) as TotalDownline'
        );
        $query->from('Placement');
        $query->filter('NLeft:GreaterThan', (int)$placement->NLeft);
        $query->filter('NRight:GreaterThan', (int)$placement->NRight);
        $query->groupby('NLevel');
        $query->orderby('NLevel');
        $items = $query->execute();

        foreach($items as $item){
            $level = $item['NLevel'] - $placement->NLevel;
            $total_downline = $item['TotalDownline'];
            $title = '<span class="level">Level '.$level.' ('.$total_downline.')</span>';
            $metadata = array(
                'Level' => $level,
                'TotalDownline' => $total_downline
            );
            $result[] = array(
                "metadata" => $metadata,
                "attr" => array("id" => "node_".$level, "level" => $level, "rel" => 'showlevel'),
                "data" => 
                    array(
                        'title' => $title,
                        'icon' => 'showlevel.jpg',
                        'attr' => ''
                    ),
                "state" => "closed"
            );
        }
        
        return $result;    
    }
    
    static function tree_node($id, $fields = null){
        $placement = DataObject::get_by_id('Placement', (int)$id);
        if(!$placement) return array();
        
        $result = array();
        $metadata = array();
        $title = '';
        $member = $placement->Member();
        if(!$fields){
            $fields = array(
                'ID' => false,
                'Username' => true, 
                'RankTitle' => true, 
                'Status.Title' => true, 
                'Name' => true, 
                'JoinedDate.Nice' => true,
                'TotalPoint.Formatted' => true,
                'TotalPlacementGroupDownline.Formatted' => true
            );
        }
        foreach($fields as $field => $show){
        	if (strstr($field, '.')) {
				$parts = explode('.', $field);
				$name = array_shift($parts);
				$func = array_shift($parts);
				$obj = $member->obj($name);
				$data = call_user_func(array($obj, $func));
			} else {
				$name = $field;
				$data = $member->{$name};
			}
			
			$metadata[$name] = $member->{$name};
			
			if($data == ''){
                $data = 'N/A';
            }

            if($show){
                $title .= "<span class='".$name." tree-title'>".$data."</span>&nbsp";
            }
        }
                    
        $result = array(
            "metadata" => $metadata,
            "attr" => array("id" => "node_".$id, "rel" => $member->RankCode ? strtolower($member->RankCode) : 'unknow'),
            "data" => 
                array(
                    'title' => $title,
                    'icon' => '',
                    'attr' => ''
                ),
            "state" => ""
        );
        
        return $result;    
    }
    
    function requireDefaultRecords(){
        $root_username = Placement::get_root_username();
        if(!$root = DataObject::get_one('Member', "Username = '".$root_username."'")){
            $rootid = Member::create()->setField('Username', $root_username)->write(); 
            $data['Placement'] = array(
                'command' => 'insert',
                'fields' => array(
                    'ClassName' => "'Placement'",
                    'Created' => "now()",
                    'LastEdited' => "now()",
                    'MemberID' => "'". $rootid ."'",
                    'PlaceID' => "'".$rootid."'",
                    'SetOption' => "'Auto Weak Leg'",
                    'ParentID' => "'0'"
                )
            );
            DB::manipulate($data);
			$this->create_preorderings();
        }
        else if(!$placement = DataObject::get_one('Placement', "MemberID = '".$root->ID."'")){
            $data['Placement'] = array(
                'command' => 'insert',
                'fields' => array(
                    'ClassName' => "'Placement'",
                    'Created' => "now()",
                    'LastEdited' => "now()",
                    'MemberID' => "'". $root->ID ."'",
                    'PlaceID' => "'".$root->ID."'",
                    'SetOption' => "'Auto Weak Leg'",
                    'ParentID' => "'0'"
                )
            );
            DB::manipulate($data);
			$this->create_preorderings();
        }
    }
    
    function setPlacement($username){
        $this->ParentID = Distributor::get_id_by_username($username);
    }
	
	function setDefaultPlace($place){
        $this->SetDefaultPlace = $place;
    }
    
    function setPosition($position){
        $this->SetPosition = $position;
    }
	
	function setPositionOption($option){
        $this->SetPositionOption = $option;
    }
    
    function onBeforeWrite(){
		if($this->SetDefaultPlace != ''){
            $this->PlaceID = Distributor::get_id_by_username($this->SetDefaultPlace);
        }
		else if(!$this->PlaceID){
            $this->PlaceID = $this->MemberID;
        }
        
        if($this->PlacementUsername != ''){
            $this->ParentID = Distributor::get_id_by_username($this->PlacementUsername);
        }
        
        if($this->ParentID && (!$this->NSeqno || $this->SetPosition || $this->SetPositionOption)){
            $parentid = $this->ParentID;
            if($this->SetPosition){
                $option = 'Manual';
            }
			else if($this->SetPositionOption){
				$option = $this->SetPositionOption;
			}
            else{
                $option = Placement::get_placement_option_by_id($this->ParentID);
            }
            switch ($option) {
                case 'Auto Left':
                    $this->outerPositionAssign($parentid, 1);
                    break;
                case 'Auto Right':
                    $this->outerPositionAssign($parentid, 2);
                    break;
                case 'Manual':
                    $this->NSeqno = $this->SetPosition;
                    break;
                case 'Auto Weak Leg':
                default:
                    $limit_child = Placement::get_direct_child_limit();
                    $count_child = Placement::get()->filter('ParentID', $parentid)->count();
                    if($count_child < $limit_child){
                    	for($seqno = 1; $seqno <= $limit_child; $seqno++) {
                    		$exists_child = Placement::get()->filter('ParentID', $parentid)->filter('NSeqno', $seqno)->count();
							if(!$exists_child){
                        		$this->NSeqno = $seqno;
								break;
							}
						}
                    }
                    else{
                        $total_downline = DB::query("select (NRight - NLeft - 1) / 2 as TotalDownline from Placement where ID = '".$parentid."'")->value();
                        $pass_parentid = $parentid;
                        $pass_seqno = 1;
                        for($seqno = 1; $seqno <= $limit_child; $seqno++) {
                            $next_total_downline = DB::query("select (NRight - NLeft - 1) / 2 as TotalDownline from Placement where ParentID = '".$parentid."' and NSeqno = '".$seqno."'")->value();
                            if($next_total_downline < $total_downline){
                                $total_downline = $next_total_downline;
                                $pass_parentid = DB::query("select ID from Placement where ParentID = '".$parentid."' and NSeqno = '".$seqno."'")->value();
                                $pass_seqno = $seqno;
                            }
                        }
                        
                        $this->outerPositionAssign($pass_parentid, $pass_seqno);
                    }
                    break;
            }
        }

        parent::onBeforeWrite();
    }

	function onAfterWrite(){
		parent::onAfterWrite();
		if(!$this->isChanged('ID') && ($this->isChanged('ParentID') || $this->isChanged('NSeqno'))){
			$data = $this->getChangedFields(true);
			ChangePlacementLog::create()->update(
				array(
					'MemberID' => $this->MemberID,
					'FromPlacementID' => $data['ParentID']['before'],
					'ToPlacementID' => $data['ParentID']['after'],
					'FromPosition' => $this->isChanged('NSeqno') ? $data['NSeqno']['before'] : $this->NSeqno,
					'ToPosition' => $this->isChanged('NSeqno') ? $data['NSeqno']['after'] : $this->NSeqno
				)
			)->write();
		}
	}

    function outerPositionAssign($parentid, $seqno){
        $this->ParentID = $parentid;
        $this->NSeqno = $seqno;
        $placementID = DB::query("select ID from Placement where NSeqno = '".$seqno."' AND ParentID = '".$parentid."'")->value();
        while($placementID > 0){
            $this->ParentID = $placementID;
            $placementID = DB::query("select ID from Placement where NSeqno = '".$seqno."' AND ParentID = '".$placementID."'")->value();
        }
    }
	
	function getTotalLeftGroupDownline(){
		$placement = Placement::get()->filter('ParentID', $this->ID)->find('NSeqno', 1);
		if($placement){
	        $left = (int)$placement->NLeft;
	        $right = (int)$placement->NRight;
	        return ($right - $left - 1)/2 + 1;
		} else {
			return 0;
		}
    }
	
	function getTotalRightGroupDownline(){
        $placement = Placement::get()->filter('ParentID', $this->ID)->find('NSeqno', 2);
		if($placement){
	        $left = (int)$placement->NLeft;
	        $right = (int)$placement->NRight;
	        return ($right - $left - 1)/2 + 1;
		} else {
			return 0;
		}
    }
	
	function provideI18nEntities() {
		$entities = parent::provideI18nEntities();
		$source = PlacementField::create('Placement')->getPositionField()->getSource();
		foreach($source as $key => $val){
			if($key){
				$entities[sprintf('%s.%s', 'Placement', strtoupper(preg_replace("/[^a-z0-9_]+/i", "", str_replace(' ', '_', $val))))] = array($val);
			}
		}

		return $entities;
	}
}

?>